#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */ 
#endregion

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.Diagnostics;
using System.Globalization;
using Adoor.Relational.SqlDriver;

namespace Adoor.Relational.RelationalSource
{
    [System.ComponentModel.ToolboxItem(true), System.ComponentModel.DesignTimeVisible(true)]    
	public class OleDbRelationalSource: CommonDbRelationalSource
	{
		public OleDbRelationalSource(ISqlDriver sqlDriver
            , OleDbConnectionStringBuilder connectionStringBuilder):
            base(OleDbFactory.Instance, sqlDriver, connectionStringBuilder)
		{
		}

		protected override void EscapeCommandText(ref string commandText, IDataParameterCollection parameters)
		{
			if (parameters.Count == 0)
			{
				commandText = string.Format(commandText); // remove {{ && }}
				return;
			}
			string declare = "";
			string setValue = "";
			string[] paramNames = new string[parameters.Count];
			int i = 0;
			foreach (IDataParameter p in parameters)
			{
				if (i++ != 0) 
					declare += ", ";
				paramNames[i - 1] = "@a" + i.ToString();
				declare += "@a" + i.ToString();
				switch (p.DbType)
				{
					case DbType.AnsiString:
					case DbType.AnsiStringFixedLength:
					case DbType.String:
					case DbType.StringFixedLength:
						declare += " varchar";
						break;
					case DbType.Int16: 
					case DbType.Int32:
					case DbType.Int64:
					case DbType.Decimal:
					case DbType.Double:
					case DbType.Single: 
					case DbType.UInt16:
					case DbType.UInt32:
					case DbType.UInt64:
					case DbType.Boolean:
						declare += " numeric";
						break;
					case DbType.Date:
					case DbType.DateTime:
					case DbType.Time:
						declare += " datetime";
						break;
					case DbType.Binary:
					case DbType.Byte:
					case DbType.Currency:
					case DbType.Guid:
					case DbType.Object: 
					case DbType.SByte:
					case DbType.VarNumeric:
					default:
						throw new NotSupportedException("type " + p.DbType + " not supported");
				}
				setValue += "set @a" + i.ToString() + " = ?; ";
			}
			commandText = "declare " + declare + " begin " + setValue + commandText + "; end";
			commandText = string.Format(commandText, paramNames);
		}

		private static bool IsInPrimaryKey(DataColumn dc, DataTable table)
		{
			foreach(DataColumn pk in table.PrimaryKey)
				if (string.Compare(pk.ColumnName, dc.ColumnName, true) == 0)
					return true;
			return false;
		}

		public OleDbCommand GetUpdateCommand(DataTable table, string updateCommand, ICollection<DataColumn> dataColumns)
		{
            OleDbCommand result = (OleDbCommand)dbProviderFactory.CreateCommand();
			foreach (DataColumn dc in dataColumns)
			{
				if (dc.Expression != "" || IsInPrimaryKey(dc, table))
					continue;
				OleDbParameter p = result.Parameters.Add(new OleDbParameter());
				//p.ParameterName = "@p" + (++i).ToString();
				p.SourceColumn = dc.ColumnName;
				p.DbType = Type2DbType(dc.DataType);
			}
			foreach(DataColumn dc in table.PrimaryKey)
			{
				OleDbParameter p = new OleDbParameter();
				//p.ParameterName = "@p" + (++i).ToString();
				p.SourceColumn = dc.ColumnName;
				p.SourceVersion = DataRowVersion.Original;
				p.DbType = Type2DbType(dc.DataType);
				result.Parameters.Add(p);
			}
			if (updateWhereAll)
				foreach (DataColumn dc in dataColumns)
				{
					OleDbParameter p = new OleDbParameter();
					//p.ParameterName = "@p" + (++i).ToString();
					p.SourceColumn = dc.ColumnName;
					p.SourceVersion = DataRowVersion.Original;
					p.DbType = Type2DbType(dc.DataType);
					result.Parameters.Add(p);
				}
			EscapeCommandText(ref updateCommand, result.Parameters);
			result.CommandText = updateCommand;
            return result;
		}

		public OleDbCommand GetInsertCommand(DataTable table, string insertCommand, ICollection<DataColumn> dataColumns)
		{
            OleDbCommand result = (OleDbCommand) dbProviderFactory.CreateCommand();
			foreach (DataColumn dc in dataColumns)
			{
				if (dc.Expression != "")
					continue;
				DbParameter p = dbProviderFactory.CreateParameter();
				//p.ParameterName = "@p" + (++i).ToString();
				p.SourceColumn = dc.ColumnName;
				p.DbType = Type2DbType(dc.DataType);
				result.Parameters.Add(p);
			}
			EscapeCommandText(ref insertCommand, result.Parameters);
			result.CommandText = insertCommand;
			return result;
		}

		public OleDbCommand GetDeleteCommand(DataTable table, string deleteCommand, ICollection<DataColumn> dataColumns)
		{
            OleDbCommand result = (OleDbCommand)dbProviderFactory.CreateCommand();
			foreach(DataColumn dc in table.PrimaryKey)
			{
                DbParameter p = dbProviderFactory.CreateParameter();
                //p.ParameterName = "@p" + (++i).ToString();
				p.SourceColumn = dc.ColumnName;
				p.SourceVersion = DataRowVersion.Original;
				p.DbType = Type2DbType(dc.DataType);
				result.Parameters.Add(p);
			}
			if (updateWhereAll) 
			{
				foreach (DataColumn dc in dataColumns)
				{
                    DbParameter p = dbProviderFactory.CreateParameter();
                    //p.ParameterName = "@p" + (++i).ToString();
					p.SourceColumn = dc.ColumnName;
					p.SourceVersion = DataRowVersion.Original;  // ignored ?
					p.DbType = Type2DbType(dc.DataType);
					result.Parameters.Add(p);
				}
			}
			EscapeCommandText(ref deleteCommand, result.Parameters);
			result.CommandText = deleteCommand;
			return result;
		}

		public override DbDataAdapter GetDataAdapter(DataTable table,
			string selectCommand, string updateCommand, string insertCommand, string deleteCommand,
			ICollection<DataColumn> dataColumns)
		{

            OleDbDataAdapter result = (OleDbDataAdapter)dbProviderFactory.CreateDataAdapter();
            result.SelectCommand = (OleDbCommand) dbProviderFactory.CreateCommand();
            result.SelectCommand.Connection = (OleDbConnection)dbProviderFactory.CreateConnection();
            result.SelectCommand.Connection.ConnectionString = ConnectionStringBuilder.ConnectionString;
            result.SelectCommand.CommandText = selectCommand;

            if(AdoorLoger.IsDebugEnabled)
			    result.RowUpdating += new OleDbRowUpdatingEventHandler(LogOnRowUpdating);

			// Update command
			result.UpdateCommand = GetUpdateCommand(table, updateCommand, dataColumns);
            result.UpdateCommand.Connection = result.UpdateCommand.Connection;
				
			// Insert command
			result.InsertCommand = GetInsertCommand(table, insertCommand, dataColumns);
            result.InsertCommand.Connection = result.UpdateCommand.Connection;
			
			// Delete command
			result.DeleteCommand = GetDeleteCommand(table, deleteCommand, dataColumns);
            result.DeleteCommand.Connection = result.UpdateCommand.Connection;
			
			return result;
		}

        public void LogOnRowUpdating(object sender, OleDbRowUpdatingEventArgs args)
		{
			AdoorLoger.Debug(Tracer.GetTrace(args.Command.CommandText, args.Command));			
		}
	}
}
